#region License
/*
 * Copyright (c) 2007, Matthew Holmes
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice, this
 * list of conditions and the following disclaimer in the documentation and/or
 * other materials provided with the distribution.
 *
 * * Neither the name of The Mango Project nor the names of its contributors may be
 * used to endorse or promote products derived from this software without specific
 * prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#endregion

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

using Mango.Framework.Common;

using Mogre;

namespace Mango.Framework.Controls {
    public partial class OgreView : UserControl, IOgreConsumer {
        #region Fields

        private static int m_ViewCount = 0;
        private bool m_Active = false;
        private bool m_InitDone = false;
        private string m_InstName = String.Empty;
        private RenderWindow m_Window = null;
        private Camera m_Camera = null;
        private Material m_Material = null;

        #endregion

        #region Constructors

        public OgreView () {
            InitializeComponent ();
            Disposed += new EventHandler (OgreView_Disposed);
        }

        #endregion

        #region Properties

        public bool IsActive {
            get {
                return (m_Active && m_Window != null);
            }
            set {
                m_Active = value;
            }
        }

        #endregion

        #region Public Methods

        public void InitializeView (bool setupModel) {
            if (Singleton.HasRef<OgreManager> ()) {
                Root root = Singleton.Ref<OgreManager> ().Root;
                SceneManager mgr = Singleton.Ref<OgreManager> ().SceneManager;

                InstanceName = GetOgreViewName ();

                NameValuePairList nvp = new NameValuePairList ();
                nvp["externalWindowHandle"] = this.Handle.ToString ();
                nvp["vsync"] = "true";
                m_Window = root.CreateRenderWindow (InstanceName, (uint)this.Width, (uint)this.Height, false, nvp);

                m_Camera = mgr.CreateCamera (InstanceName + ".Camera");
                m_Camera.AutoAspectRatio = true;
                m_Window.AddViewport (m_Camera);

                m_Camera.SetPosition (0.0f, 0.0f, 0.0f);
                IsActive = true;

                Singleton.Ref<OgreManager> ().RegisterConsumer (this);
                m_InitDone = true;

                if (setupModel) {
                    Entity ent = mgr.CreateEntity (InstanceName + ".Mesh", SceneManager.PrefabType.PT_SPHERE);
                    mgr.RootSceneNode.AttachObject (ent);
                    m_Camera.LookAt (ent.BoundingBox.Center);

                    m_Camera.SetPosition (0.0f, 0.0f, ent.BoundingBox.HalfSize.z * 3);
                }
            }
        }

        #endregion

        #region Protected Methods

        protected override void OnPaintBackground (PaintEventArgs e) {
            if(IsActive) {
                e.Graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
                m_Window.Update ();
            }
            else {
                base.OnPaintBackground (e);
            }
        }

        #endregion

        #region Private Methods

        private void OgreView_Disposed (object sender, EventArgs e) {
            m_Window = null;
            m_Camera = null;
            m_Active = false;
        }

        private static string GetOgreViewName() {
            return String.Format ("OgreView.Instance{0}", m_ViewCount++);
        }

        private void OgreView_Resize (object sender, EventArgs e) {
            if (!m_InitDone) {
                if(this.Width > 0 && this.Height > 0) {
                    InitializeView (true);
                }
            }

            if (IsActive) {
                m_Window.WindowMovedOrResized ();
                Invalidate ();
            }
        }

        private void OgreView_Move (object sender, EventArgs e) {
            if (IsActive) {
                m_Window.WindowMovedOrResized ();
                Invalidate ();
            }
        }

        private void SetMaterial(Material mat) {
            m_Material = mat;
            SceneManager mgr = Singleton.Ref<OgreManager> ().SceneManager;
            Entity ent = mgr.GetEntity (InstanceName + ".Mesh");
            MeshPtr mesh = ent.GetMesh ();
            for(ushort i = 0; i < mesh.NumSubMeshes; i++) {
                mesh.GetSubMesh (i).MaterialName = mat.Name;
            }
        }

        #endregion

        #region IOgreConsumer Members

        public string InstanceName {
            get {
                return m_InstName;
            }
            private set {
                m_InstName = value;
            }
        }

        public Material Material {
            get {
                return m_Material;
            }
            set {
                SetMaterial (value);
            }
        }

        #endregion
    }
}
